SPB Git forge
3commits 1branches 0releases
417.0 KBsize
maindefault branch
10 days agolast push
TypeScript 66.5% Python 30.9% JavaScript 1.4% CSS 0.7%
4.5 KB · 98 lines tsx
Raw Blame History
1import type { Metadata } from 'next';2import Link from 'next/link';3import { EnabledToggle, RunNowButton } from '@/components/admin/actions';4import { AdminError, AdminHeader, JsonInline, RunsTable, SectionTitle } from '@/components/admin/ui';5import { Pagination } from '@/components/ui/pagination';6import { adminApi, safeAdmin } from '@/lib/admin-api';7import { fmtAgo, fmtDateTime } from '@/lib/format';89export async function generateMetadata({ params }: { params: Promise<{ name: string }> }): Promise<Metadata> {10  const { name } = await params;11  return { title: `${name} runs` };12}1314export default async function AdminConnectorPage({ params, searchParams }: { params: Promise<{ name: string }>; searchParams: Promise<{ page?: string }> }) {15  const { name } = await params;16  const sp = await searchParams;17  const page = Math.max(1, Number(sp.page) || 1);18  const [runsRes, overviewRes] = await Promise.all([safeAdmin(adminApi.connectorRuns(name, page, 50)), safeAdmin(adminApi.overview())]);19  const connector = overviewRes.data?.data.connectors.find((c) => c.name === name) ?? null;20  const now = Date.now();21  return (22    <>23      <AdminHeader24        title={<span className="mono">{name}</span>}25        lede={connector ? `${connector.source_name} · ${connector.description ?? ''}` : 'Connector runs and errors'}26        action={27          <div className="flex flex-wrap items-center gap-2">28            <RunNowButton name={name} />29            {connector && <EnabledToggle name={name} enabled={connector.enabled} />}30            <Link href="/admin" className="text-sm text-accent hover:underline">31              ← Overview32            </Link>33          </div>34        }35      />36      {connector && (37        <dl className="grid grid-cols-2 gap-3 text-sm md:grid-cols-4">38          <div>39            <dt className="eyebrow">Enabled</dt>40            <dd className={connector.enabled ? 'text-active' : 'text-warn'}>{connector.enabled ? 'yes' : 'no'}</dd>41          </div>42          <div>43            <dt className="eyebrow">Last success</dt>44            <dd title={fmtDateTime(connector.last_success_at)}>{fmtAgo(connector.last_success_at, now)}</dd>45          </div>46          <div>47            <dt className="eyebrow">Last attempt</dt>48            <dd title={fmtDateTime(connector.last_attempt_at)}>{fmtAgo(connector.last_attempt_at, now)}</dd>49          </div>50          <div>51            <dt className="eyebrow">Next run</dt>52            <dd>{fmtDateTime(connector.next_run_at)}</dd>53          </div>54          <div className="col-span-2 md:col-span-4">55            <dt className="eyebrow">Config</dt>56            <dd>57              <JsonInline value={connector.config} />58            </dd>59          </div>60        </dl>61      )}6263      {runsRes.error !== null ? (64        <div className="mt-6">65          <AdminError message={runsRes.error} />66        </div>67      ) : (68        <>69          <SectionTitle>Runs</SectionTitle>70          <RunsTable runs={runsRes.data.data} now={now} showConnector={false} emptyLabel="This connector has never run" />71          <Pagination className="mt-4" page={runsRes.data.pagination.page} pages={runsRes.data.pagination.pages} total={runsRes.data.pagination.total} pageSize={runsRes.data.pagination.page_size} makeHref={(p) => `/admin/connectors/${encodeURIComponent(name)}?page=${p}`} />7273          <SectionTitle>Recent errors</SectionTitle>74          {runsRes.data.errors.length === 0 ? (75            <p className="rounded-md border border-dashed border-rule-strong px-4 py-6 text-center text-sm text-ink-3">No errors recorded</p>76          ) : (77            <ul className="divide-y divide-rule border-y border-rule">78              {runsRes.data.errors.map((e) => (79                <li key={e.id} className="grid gap-1 py-2.5 text-sm sm:grid-cols-[160px_140px_minmax(0,1fr)] sm:gap-4">80                  <span className="text-ink-2" title={fmtDateTime(e.occurred_at)}>81                    {fmtAgo(e.occurred_at, now)}82                  </span>83                  <span className="mono text-xs text-danger">{e.error_type ?? 'Error'}</span>84                  <span className="min-w-0">85                    <span className="mono break-words text-xs text-ink">{e.message ?? '—'}</span>86                    {e.run_id && <span className="mono ml-2 text-[11px] text-ink-3">{e.run_id}</span>}87                    {e.context && <JsonInline value={e.context} className="mt-1" />}88                  </span>89                </li>90              ))}91            </ul>92          )}93        </>94      )}95    </>96  );97}98